home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sprite 1984 - 1993
/
Sprite 1984 - 1993.iso
/
src
/
kernel
/
proc
/
procMigrate.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-18
|
80KB
|
2,889 lines
/*
* procMigrate.c --
*
* Routines for process migration. These provide the system
* call interface to initiate migration and routines to transfer
* data from the host on which the process is currently executing
* to the host to which it is migrating. The routines that accept
* this data are in procRemote.c.
*
* Copyright 1986, 1988, 1989 Regents of the University of California
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*/
#ifndef lint
static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/proc/procMigrate.c,v 9.36 92/06/01 14:39:23 kupfer Exp $ SPRITE (Berkeley)";
#endif /* not lint */
#include <sprite.h>
#include <mach.h>
#include <proc.h>
#include <procInt.h>
#include <procMigrate.h>
#include <migrate.h>
#include <migVersion.h>
#include <fs.h>
#include <stdlib.h>
#include <string.h>
#include <sig.h>
#include <spriteTime.h>
#include <trace.h>
#include <list.h>
#include <byte.h>
#include <vm.h>
#include <sys.h>
#include <dbg.h>
#include <rpc.h>
#include <prof.h>
#include <sched.h>
#include <sync.h>
#include <sysSysCall.h>
#include <timer.h>
#include <stdio.h>
#include <bstring.h>
#include <recov.h>
Sync_Condition migrateCondition;
Sync_Condition evictCondition;
static Sync_Lock migrateLock = Sync_LockInitStatic("Proc:migrateLock");
#define LOCKPTR &migrateLock
static Time timeEvictionStarted;
int proc_MigDebugLevel = 2;
Trace_Header proc_TraceHeader;
Trace_Header *proc_TraceHdrPtr = (Trace_Header *)NIL;
Boolean proc_DoTrace = FALSE;
Boolean proc_DoCallTrace = FALSE;
/*
* Allocate variables and structures relating to statistics.
* Updating variables is done under a monitor. Currently, each update
* is typically done via a monitored procedure call, though it may be
* preferable to in-line the monitors (if this is permissible at some point)
* or combine multiple operations in a single procedure.
* Some of the statistics are kept even in CLEAN kernels because they affect
* the kernel's notion of whether eviction is necessary. Also, the timing
* statistics are useful for benchmarking CLEAN kernels. Things that are
* purely for statistics gathering are conditioned on CLEAN as well as
* proc_MigDoStats.
*/
Boolean proc_MigDoStats = TRUE;
Proc_MigStats proc_MigStats;
/*
* True if we should convert a SIG_DEBUG into a SIG_KILL for migrated
* processes.
*/
Boolean proc_KillMigratedDebugs = TRUE;
int proc_AllowMigrationState = PROC_MIG_ALLOW_DEFAULT;
/*
* Declare some variables from corresponding constants. This permits
* them to be modified using the debugger or mainHook.
*/
int proc_MigrationVersion = PROC_MIGRATE_VERSION;
static int statsVersion = PROC_MIG_STATS_VERSION;
/*
* Procedures internal to this file
*/
static ReturnStatus GetProcEncapSize _ARGS_((Proc_ControlBlock *procPtr,
int hostID, Proc_EncapInfo *infoPtr));
static ReturnStatus EncapProcState _ARGS_((Proc_ControlBlock *procPtr,
int hostID, Proc_EncapInfo *infoPtr,
Address bufPtr));
static ReturnStatus DeencapProcState _ARGS_((Proc_ControlBlock *procPtr,
Proc_EncapInfo *infoPtr, Address bufPtr));
static void AbortMigration _ARGS_((Proc_ControlBlock *procPtr));
static void SuspendCallback _ARGS_((ClientData data,
Proc_CallInfo *callInfoPtr));
/*
* Procedures for statistics gathering
*/
static ENTRY void AddMigrateTime _ARGS_((Time time, unsigned int *totalPtr,
unsigned int *squaredTotalPtr));
static ENTRY void AccessStats _ARGS_((Proc_MigStats *copyPtr));
static ENTRY Boolean EvictionStarted _ARGS_((void));
static ENTRY void WaitForEviction _ARGS_((void));
static ENTRY ReturnStatus WaitForMigration _ARGS_((void));
#ifdef DEBUG
int proc_MemDebug = 0;
#endif /* DEBUG */
/*
* Define the structure for keeping track of callbacks for migrating
* a process. (This is done after procedure declarations since
* some things are static and we need the forward reference.)
*
* See the comments in Proc_MigrateTrap for further explanation.
*/
typedef struct {
ReturnStatus (*preFunc)(); /* function to call when initiating
migration (returning size); should not
have side-effects requiring further
callback */
ReturnStatus (*encapFunc)(); /* function to call to encapsulate data */
ReturnStatus (*deencapFunc)(); /* function to call to deencapsulate
data on other end */
ReturnStatus (*postFunc)(); /* function to call after migration
completes or fails */
Proc_EncapToken token; /* identifier to match encap and deencap
functions between two hosts */
int whenNeeded; /* flags defined below indicate when
needed */
} EncapCallback;
/*
* Flags for the whenNeeded field:
*/
#define MIG_ENCAP_MIGRATE 1
#define MIG_ENCAP_EXEC 2
#define MIG_ENCAP_ALWAYS (MIG_ENCAP_MIGRATE | MIG_ENCAP_EXEC)
/*
* Set up the functions to be called.
*/
static EncapCallback encapCallbacks[] = {
{ GetProcEncapSize, EncapProcState, DeencapProcState, NULL,
PROC_MIG_ENCAP_PROC, MIG_ENCAP_ALWAYS},
{ ProcExecGetEncapSize, ProcExecEncapState, ProcExecDeencapState,
ProcExecFinishMigration,
PROC_MIG_ENCAP_EXEC, MIG_ENCAP_EXEC},
#ifdef notdef
{ Vm_InitiateMigration, Vm_EncapState, Vm_DeencapState, Vm_FinishMigration,
PROC_MIG_ENCAP_VM, MIG_ENCAP_MIGRATE},
#endif
{ Vm_InitiateMigration, Vm_EncapState, Vm_DeencapState, NULL,
PROC_MIG_ENCAP_VM, MIG_ENCAP_MIGRATE},
{ Fs_InitiateMigration, Fs_EncapFileState, Fs_DeencapFileState, NULL,
PROC_MIG_ENCAP_FS, MIG_ENCAP_ALWAYS},
{ Mach_GetEncapSize, Mach_EncapState, Mach_DeencapState, NULL,
PROC_MIG_ENCAP_MACH, MIG_ENCAP_ALWAYS},
{ Prof_GetEncapSize, Prof_EncapState, Prof_DeencapState, NULL,
PROC_MIG_ENCAP_PROF, MIG_ENCAP_ALWAYS},
{ Sig_GetEncapSize, Sig_EncapState, Sig_DeencapState, NULL,
PROC_MIG_ENCAP_SIG, MIG_ENCAP_ALWAYS},
};
#define BREAKS_KDBX
#ifdef BREAKS_KDBX
static struct {
char *preFunc; /* name of function to call when initiating
migration */
char *encapFunc; /* name of function to call to encapsulate */
char *deencapFunc; /* name of function to call to deencapsulate */
char *postFunc; /* name of function to call when done */
} callbackNames[] = {
{ "GetProcEncapSize", "EncapProcState", "DeencapProcState", NULL},
{ "ProcExecGetEncapSize", "ProcExecEncapState", "ProcExecDeencapState", "ProcExecFinishMigration"},
{ "Vm_InitiateMigration", "Vm_EncapState", "Vm_DeencapState",
NULL},
{ "Fs_InitiateMigration", "Fs_EncapFileState", "Fs_DeencapFileState",
"Fs_MigDone"},
{ "Mach_InitiateMigration", "Mach_EncapState", "Mach_DeencapState", NULL},
{ "Prof_InitiateMigration", "Prof_EncapState", "Prof_DeencapState", NULL},
{ "Sig_InitiateMigration", "Sig_EncapState", "Sig_DeencapState", NULL}
};
#endif
/*
* Define a macro for squaring a time without automatically overflowing
* due to the large number of microseconds being multiplied.
* The square of (X + Y/1000000) is X^2 + 2XY/1000000 + Y^2/10^12.
* XXX not used -- switched to single integers as milliseconds.
*/
#define SQUARE_TIME(time, squaredTime) \
squaredTime.seconds = time.seconds * time.seconds; \
squaredTime.microseconds = 2 * time.seconds * time.microseconds + \
time.microseconds * time.microseconds / 1000000; \
while (squaredTime.microseconds > 1000000) { \
squaredTime.seconds++; \
squaredTime.microseconds -= 1000000; \
} \
/*
*----------------------------------------------------------------------
*
* Proc_MigInit --
*
* Initialize data structures relating to process migration.
* This procedure is called at boot time.
* Sets up statistics gathering and recovery code.
*
* Results:
* None.
*
* Side effects:
* Calls other initialization procedures.
*
*----------------------------------------------------------------------
*/
void
Proc_MigInit()
{
AccessStats((Proc_MigStats *) NIL);
ProcRecovInit();
}
/*
*----------------------------------------------------------------------
*
* Proc_Migrate --
*
* Migrates a process to another workstation. The process may be
* PROC_MY_PID, PROC_ALL_PROCESSES, or a process ID.
* PROC_ALL_PROCESSES implies evicting all foreign processes, in
* which case the hostID is ignored. The workstation may be
* PROC_MIG_ANY or a particular workstation. (For now, the
* workstation argument must be a specific workstation.)
*
* This procedure implements the system call by the same name.
*
* Results:
* PROC_INVALID_PID - the pid argument was illegal.
* PROC_INVALID_NODE_ID - the host argument was illegal.
* GEN_NO_PERMISSION - the user or process is not permitted to
* migrate.
* Other results may be returned from the VM and RPC modules.
*
* Side effects:
* The specified process is migrated to another workstation.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Proc_Migrate(pid, hostID)
Proc_PID pid;
int hostID;
{
register Proc_ControlBlock *procPtr;
ReturnStatus status;
Proc_TraceRecord record;
Boolean migrateSelf = FALSE;
int permMask;
/*
* It is possible for a process to try to migrate onto the machine
* on which it is currently executing.
*/
if (hostID == rpc_SpriteID) {
return(SUCCESS);
}
if (Proc_ComparePIDs(pid, PROC_ALL_PROCESSES)) {
procPtr = Proc_GetEffectiveProc();
if (procPtr->effectiveUserID != 0) {
return(GEN_NO_PERMISSION);
}
status = Proc_EvictForeignProcs();
return(status);
}
if (hostID <= 0 || hostID > NET_NUM_SPRITE_HOSTS) {
return(GEN_INVALID_ARG);
}
if (Proc_ComparePIDs(pid, PROC_MY_PID)) {
migrateSelf = TRUE;
procPtr = Proc_GetActualProc();
if (procPtr == (Proc_ControlBlock *) NIL) {
panic("Proc_Migrate: procPtr == NIL\n");
}
Proc_Lock(procPtr);
pid = procPtr->processID;
} else {
procPtr = Proc_LockPID(pid);
if (procPtr == (Proc_ControlBlock *) NIL) {
return (PROC_INVALID_PID);
}
}
if (proc_MigDebugLevel > 3) {
printf("Proc_Migrate: migrate process %x to host %d.\n",
procPtr->processID, hostID);
}
/*
* Do some sanity checking.
*/
if ((procPtr->state == PROC_DEAD) || (procPtr->state == PROC_EXITING) ||
(procPtr->genFlags & PROC_DYING)) {
if (proc_MigDebugLevel > 3) {
printf("Proc_Migrate: process %x has exited.\n",
pid);
}
Proc_Unlock(procPtr);
return(PROC_INVALID_PID);
}
if (procPtr->state == PROC_MIGRATED) {
if (proc_MigDebugLevel > 1) {
printf("Proc_Migrate: process %x has already migrated.\n",
pid);
}
Proc_Unlock(procPtr);
return(PROC_INVALID_PID);
}
if (procPtr->genFlags & PROC_FOREIGN) {
if (proc_MigDebugLevel > 0) {
printf("Proc_Migrate: process %x is foreign... can't migrate yet.\n",
procPtr->processID);
}
Proc_Unlock(procPtr);
return(PROC_INVALID_PID);
}
if (procPtr->genFlags & PROC_DONT_MIGRATE) {
if (proc_MigDebugLevel > 0) {
printf("Proc_Migrate: process %x is not allowed to migrate.\n",
pid);
}
Proc_Unlock(procPtr);
return(GEN_NO_PERMISSION);
}
if (procPtr->argString == (char *) NIL) {
if (proc_MigDebugLevel > 0) {
printf("Proc_Migrate: process %x has no argument string: can't migrate.\n",
pid);
}
Proc_Unlock(procPtr);
return(PROC_INVALID_PID);
}
if (procPtr->userID == PROC_SUPER_USER_ID) {
permMask = PROC_MIG_EXPORT_ROOT;
} else {
permMask = PROC_MIG_EXPORT_ALL;
}
if ((proc_AllowMigrationState & permMask) != permMask) {
if (proc_MigDebugLevel > 0) {
printf("Proc_Migrate: user does not have permission to migrate.\n");
}
Proc_Unlock(procPtr);
return(GEN_NO_PERMISSION);
}
#ifndef CLEAN
if (proc_DoTrace && proc_MigDebugLevel > 0) {
record.processID = procPtr->processID;
record.flags = PROC_MIGTRACE_START | PROC_MIGTRACE_HOME;
record.info.filler = NIL;
Trace_Insert(proc_TraceHdrPtr, PROC_MIGTRACE_BEGIN_MIG,
(ClientData) &record);
}
#endif /* CLEAN */
/*
* Contact the remote workstation to establish communication and
* verify that migration is permissible.
*/
status = ProcInitiateMigration(procPtr, hostID);
if (status != SUCCESS) {
Proc_Unlock(procPtr);
#ifndef CLEAN
if (proc_MigDoStats) {
PROC_MIG_INC_STAT(errors);
}
#endif /* CLEAN */
return(status);
}
Proc_FlagMigration(procPtr, hostID, FALSE);
Proc_Unlock(procPtr);
/*
* If migrating another process, wait for it to migrate.
*/
if (!migrateSelf) {
status = Proc_WaitForMigration(pid);
} else {
status = SUCCESS;
}
/*
* Note: the "end migration" trace record is inserted by the MigrateTrap
* routine to get around the issue of waiting for oneself to migrate.
* (Otherwise, since we can't wait here for the current process to
* migrate -- only a different process -- we wouldn't be able to insert
* the trace record at the right time.)
*/
return(status);
}
/*
*----------------------------------------------------------------------
*
* Proc_MigrateTrap --
*
* Transfer the state of a process once it has reached a state with no
* relevant information on the kernel stack. This is done following a
* kernel call, when the process can migrate and immediately return
* to user mode and there is no relevant information on the kernel
* stack.
*
* Results:
* No value is returned.
*
* Side effects:
* The process state is transferred by performing callbacks
* to each module with state to transfer.
*
*----------------------------------------------------------------------
*/
void
Proc_MigrateTrap(procPtr)
register Proc_ControlBlock *procPtr; /* The process being migrated */
{
int hostID; /* host to which it migrates */
ReturnStatus status = FAILURE;
Proc_TraceRecord record;
Boolean foreign = FALSE;
Boolean evicting = FALSE;
Address buffer;
Address bufPtr;
int bufSize;
register int i;
Proc_EncapInfo info[PROC_MIG_NUM_CALLBACKS];
Proc_EncapInfo *infoPtr;
ProcMigCmd cmd;
Proc_MigBuffer inBuf;
int failed;
Time startTime;
Time endTime;
Time timeDiff;
unsigned int *timePtr;
unsigned int *squaredTimePtr;
int whenNeeded;
Boolean exec;
Proc_PID pid;
Proc_Lock(procPtr);
if (procPtr->genFlags & PROC_FOREIGN) {
foreign = TRUE;
if (procPtr->migFlags & PROC_EVICTING) {
evicting = TRUE;
}
}
if (procPtr->genFlags & PROC_REMOTE_EXEC_PENDING) {
whenNeeded = MIG_ENCAP_EXEC;
exec = TRUE;
} else {
whenNeeded = MIG_ENCAP_MIGRATE;
exec = FALSE;
}
if (proc_MigDoStats) {
Timer_GetTimeOfDay(&startTime, (int *) NIL, (Boolean *) NIL);
}
pid = procPtr->processID;
if (proc_DoTrace && proc_MigDebugLevel > 1) {
record.processID = pid;
record.flags = PROC_MIGTRACE_START;
if (!foreign) {
record.flags |= PROC_MIGTRACE_HOME;
}
record.info.filler = NIL;
Trace_Insert(proc_TraceHdrPtr, PROC_MIGTRACE_MIGTRAP,
(ClientData) &record);
}
procPtr->genFlags = (procPtr->genFlags
& ~(PROC_MIG_PENDING | PROC_MIGRATION_DONE)
| PROC_MIGRATING);
hostID = procPtr->peerHostID;
bufSize = 0;
/*
* Go through the list of callbacks to generate the size of the buffer
* we'll need. In unusual circumstances, a caller may return a status
* other than SUCCESS. In this case, the process should be continuable!
*/
for (i = 0; i < PROC_MIG_NUM_CALLBACKS; i++) {
if (!(encapCallbacks[i].whenNeeded & whenNeeded)) {
continue;
}
infoPtr = &info[i];
infoPtr->token = encapCallbacks[i].token;
infoPtr->processed = 0;
infoPtr->special = 0;
if (proc_MigDebugLevel > 5) {
printf("Calling preFunc %d\n", i);
}
status = (*encapCallbacks[i].preFunc)(procPtr, hostID, infoPtr);
if (status == SUCCESS && infoPtr->special) {
/*
* This is where we'd like to handle special cases like shared
* memory processes. For now, bail out.
*/
status = GEN_NOT_IMPLEMENTED;
}
if (status != SUCCESS) {
printf("Warning: Proc_MigrateTrap: error returned by encapsulation procedure %s:\n\t%s.\n",
#ifdef BREAKS_KDBX
callbackNames[i].preFunc,
#else /* BREAKS_KDBX */
"(can't get name)",
#endif /* BREAKS_KDBX */
Stat_GetMsg(status));
if (exec) {
goto failure;
} else {
AbortMigration(procPtr);
}
return;
}
bufSize += infoPtr->size + sizeof(Proc_EncapInfo);
}
if (proc_MigDebugLevel > 5) {
printf("Buffer size is %d\n", bufSize);
}
buffer = malloc(bufSize);
if (proc_MigDebugLevel > 5) {
printf("past malloc\n", bufSize);
}
bufPtr = buffer;
failed = 0;
/*
* This time, go through the list of callbacks to fill in the
* encapsulated data. From this point on, failed indicates
* that the process will be killed.
*/
for (i = 0; i < PROC_MIG_NUM_CALLBACKS; i++) {
if (!(encapCallbacks[i].whenNeeded & whenNeeded)) {
continue;
}
infoPtr = &info[i];
bcopy((Address) infoPtr, bufPtr, sizeof(Proc_EncapInfo));
bufPtr += sizeof(Proc_EncapInfo);
if (proc_MigDebugLevel > 5) {
printf("Calling encapFunc %d\n", i);
}
status = (*encapCallbacks[i].encapFunc)(procPtr, hostID, infoPtr,
bufPtr);
#ifdef lint
status = EncapProcState(procPtr, hostID, infoPtr, bufPtr);
status = ProcExecEncapState(procPtr, hostID, infoPtr, bufPtr);
status = Vm_EncapState(procPtr, hostID, infoPtr, bufPtr);
status = Fs_EncapFileState(procPtr, hostID, infoPtr, bufPtr);
status = Mach_EncapState(procPtr, hostID, infoPtr, bufPtr);
status = Prof_EncapState(procPtr, hostID, infoPtr, bufPtr);
status = Sig_EncapState(procPtr, hostID, infoPtr, bufPtr);
#endif /* lint */
if (status != SUCCESS) {
printf("Warning: Proc_MigrateTrap: error returned by encapsulation procedure %s:\n\t%s.\n",
#ifdef BREAKS_KDBX
callbackNames[i].encapFunc,
#else /* BREAKS_KDBX */
"(can't get name)",
#endif /* BREAKS_KDBX */
Stat_GetMsg(status));
failed = 1;
break;
}
bufPtr += infoPtr->size;
infoPtr->processed = 1;
}
Proc_Unlock(procPtr);
/*
* Send the encapsulated data in the buffer to the other host.
*/
if (!failed) {
/*
* Set up for the RPC.
*/
cmd.command = PROC_MIGRATE_CMD_ENTIRE;
cmd.remotePid = procPtr->peerProcessID;
inBuf.size = bufSize;
inBuf.ptr = buffer;
if (proc_MigDoStats) {
Proc_MigAddToCounter((bufSize + 1023) / 1024, &proc_MigStats.varStats.rpcKbytes, &proc_MigStats.squared.rpcKbytes);
}
if (proc_MigDebugLevel > 5) {
printf("Sending encapsulated state.\n");
}
status = ProcMigCommand(procPtr->peerHostID, &cmd, &inBuf,
(Proc_MigBuffer *) NIL);
if (status != SUCCESS) {
printf("Warning: Proc_MigrateTrap: error encountered sending encapsulated state:\n\t%s.\n",
Stat_GetMsg(status));
failed = 1;
}
}
/*
* Finally, go through the list of callbacks to clean up. Only call
* routines that were processed last time (in the case of failure
* partway through). Note that the process pointer is UNLOCKED
* during these callbacks (as well as the RPC to transfer state).
* This is primarily because Vm_FinishMigration is the only callback
* so far and it needs it unlocked, and we have to unlock for the rpc
* anyway so that we don't deadlock on the process once the migrated
* version resumes (a side effect of the RPC).
*/
bufPtr = buffer;
for (i = 0; i < PROC_MIG_NUM_CALLBACKS; i++) {
if (!(encapCallbacks[i].whenNeeded & whenNeeded)) {
continue;
}
infoPtr = &info[i];
if (infoPtr->processed != 1) {
continue;
}
bufPtr += sizeof(Proc_EncapInfo);
if (encapCallbacks[i].postFunc != NULL) {
if (proc_MigDebugLevel > 5) {
printf("Calling postFunc %d\n", i);
}
status = (*encapCallbacks[i].postFunc)(procPtr, hostID, infoPtr,
bufPtr, failed);
#ifdef lint
status = Vm_FinishMigration(procPtr, hostID, infoPtr, bufPtr,
failed);
#endif /* lint */
}
if (status != SUCCESS) {
failed = 1;
}
bufPtr += infoPtr->size;
}
free(buffer);
if (failed) {
goto failure;
}
Proc_Lock(procPtr);
procPtr->genFlags = (procPtr->genFlags
& ~(PROC_REMOTE_EXEC_PENDING| PROC_MIG_ERROR)
| PROC_MIGRATION_DONE);
Proc_Unlock(procPtr);
/*
* Tell the other host to resume the process.
*/
cmd.command = PROC_MIGRATE_CMD_RESUME;
cmd.remotePid = procPtr->peerProcessID;
if (proc_MigDebugLevel > 5) {
printf("Issuing resume command.\n");
}
status = ProcMigCommand(procPtr->peerHostID, &cmd, (Proc_MigBuffer *) NIL,
(Proc_MigBuffer *) NIL);
if (status != SUCCESS) {
printf("Warning: Proc_MigrateTrap: error encountered resuming process:\n\t%s.\n",
Stat_GetMsg(status));
goto failure;
}
/*
* If not migrating back home, note the dependency on the other host.
* Otherwise, forget the dependency after eviction.
*/
if (!foreign) {
ProcMigAddDependency(procPtr->processID, procPtr->peerProcessID);
} else {
ProcMigRemoveDependency(procPtr->processID, TRUE);
}
/*
* Anyone waiting to send a signal to the process should wait until
* this point so the process is executing on the other host. On the
* other hand, some code wants to wait until the process has finished
* context switching. So, clear the "evicting" flag, but don't yet
* clear the "migrating" flag or wake up processes waiting for the
* migration to complete.
*/
Proc_Lock(procPtr);
procPtr->migFlags &= ~PROC_EVICTING;
Proc_Unlock(procPtr);
if (proc_DoTrace && proc_MigDebugLevel > 1) {
record.flags = (foreign ? 0 : PROC_MIGTRACE_HOME);
Trace_Insert(proc_TraceHdrPtr, PROC_MIGTRACE_MIGTRAP,
(ClientData) &record);
}
if (proc_MigDoStats) {
Timer_GetTimeOfDay(&endTime, (int *) NIL, (Boolean *) NIL);
Time_Subtract(endTime, startTime, &timeDiff);
if (whenNeeded == MIG_ENCAP_MIGRATE) {
if (evicting) {
timePtr = &proc_MigStats.varStats.timeToEvict;
squaredTimePtr = &proc_MigStats.squared.timeToEvict;
} else {
timePtr = &proc_MigStats.varStats.timeToMigrate;
squaredTimePtr = &proc_MigStats.squared.timeToMigrate;
}
} else {
timePtr = &proc_MigStats.varStats.timeToExec;
squaredTimePtr = &proc_MigStats.squared.timeToExec;
}
AddMigrateTime(timeDiff, timePtr, squaredTimePtr);
}
if (proc_DoTrace && proc_MigDebugLevel > 0 && !foreign) {
record.processID = pid;
record.flags = PROC_MIGTRACE_HOME;
record.info.filler = NIL;
Trace_Insert(proc_TraceHdrPtr, PROC_MIGTRACE_END_MIG,
(ClientData) &record);
}
/*
* All aboard!
*
* Check for asynchronous errors coming in after we resumed on the other
* host. If there aren't any, mark the migration as finally really
* being complete and wake up any processes that might be waiting for
* the migration to complete.
*/
Proc_Lock(procPtr);
if (procPtr->genFlags & PROC_MIG_ERROR) {
Proc_Unlock(procPtr);
goto failure;
}
procPtr->genFlags &= ~PROC_MIGRATING;
ProcMigWakeupWaiters();
if (foreign) {
PROC_MIG_DEC_STAT(foreign);
if (evicting ||
(proc_MigStats.foreign == 0 &&
proc_MigStats.evictionsInProgress != 0)) {
ProcMigEvictionComplete();
}
#ifndef CLEAN
if (proc_MigDoStats) {
if (!evicting) {
PROC_MIG_INC_STAT(migrationsHome);
}
}
#endif /* CLEAN */
Proc_Unlock(procPtr);
ProcExitProcess(procPtr, -1, -1, -1, TRUE);
} else {
#ifndef CLEAN
if (proc_MigDoStats) {
PROC_MIG_INC_STAT(remote);
PROC_MIG_INC_STAT(exports);
if (exec) {
PROC_MIG_INC_STAT(execs);
}
PROC_MIG_INC_STAT(hostCounts[hostID]);
}
#endif /* CLEAN */
Proc_UnlockAndSwitch(procPtr, PROC_MIGRATED);
}
panic("Proc_MigrateTrap: returned from context switch.\n");
return;
failure:
/*
* If the process hit some error (e.g., the remote host rebooted), or
* the processes exited on the other host, we don't bother sending an
* RPC to the other host.
*/
Proc_Lock(procPtr);
if (!(procPtr->genFlags & PROC_MIG_ERROR)) {
ProcMigKillRemoteCopy((ClientData) procPtr->peerProcessID,
(Proc_CallInfo *) NIL);
}
procPtr->genFlags &= ~(PROC_MIGRATING|PROC_REMOTE_EXEC_PENDING|
PROC_MIG_ERROR|PROC_MIGRATION_DONE);
procPtr->migFlags &= ~PROC_EVICTING;
ProcMigWakeupWaiters();
if (proc_DoTrace && proc_MigDebugLevel > 0 && !foreign) {
record.processID = pid;
record.flags = PROC_MIGTRACE_HOME;
record.info.filler = NIL;
Trace_Insert(proc_TraceHdrPtr, PROC_MIGTRACE_END_MIG,
(ClientData) &record);
}
#ifndef CLEAN
if (proc_MigDoStats) {
PROC_MIG_INC_STAT(errors);
}
#endif /* CLEAN */
/*
* The migration failed, so exit. By calling the exit routine
* directly we avoid problems that might result from having no
* VM, etc.
*/
Proc_Unlock(procPtr);
Proc_ExitInt(PROC_TERM_DESTROYED, (int) PROC_NO_PEER, 0);
}
/*
*----------------------------------------------------------------------
*
* AbortMigration --
*
* Undo a migration at a point when the process can still be
* continued on the local host. This is only true when migrating
* a running process, not when execing a new image, since we can't
* recover from that.
*
* Results:
* None.
*
* Side effects:
* If the process is currently local, an RPC is sent to the remote host.
*
*----------------------------------------------------------------------
*/
static void
AbortMigration(procPtr)
Proc_ControlBlock *procPtr; /* ptr to process control block */
{
if (!(procPtr->genFlags & PROC_FOREIGN)) {
ProcMigKillRemoteCopy((ClientData) procPtr->peerProcessID,
(Proc_CallInfo *) NIL);
procPtr->peerProcessID = NIL;
procPtr->peerHostID = NIL;
}
procPtr->genFlags &= ~PROC_MIGRATING;
procPtr->sigPendingMask &= ~Sig_NumberToMask(SIG_MIGRATE_TRAP);
Proc_Unlock(procPtr);
ProcMigWakeupWaiters();
}
/*
*----------------------------------------------------------------------
*
* ProcMigReceiveProcess --
*
* Receive the encapsulated state of a process from another host
* and deencapsulate it by calling the appropriate callback routines.
* If deencapsulation succeeds, resume the migrated process.
*
* Results:
* A ReturnStatus indicates whether deencapsulation succeeds. If
* a module returns an error, the first error is returned and the
* rest of the state is not deencapsulated.
*
* Side effects:
* The process is resumed on this host.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
ReturnStatus
ProcMigReceiveProcess(cmdPtr, procPtr, inBufPtr, outBufPtr)
ProcMigCmd *cmdPtr;/* contains ID of process on this host */
Proc_ControlBlock *procPtr; /* ptr to process control block */
Proc_MigBuffer *inBufPtr; /* input buffer */
Proc_MigBuffer *outBufPtr; /* output buffer (stays NIL) */
{
ReturnStatus status = SUCCESS;
Address bufPtr;
register int i;
Proc_EncapInfo *infoPtr;
Proc_Lock(procPtr);
procPtr->genFlags = (procPtr->genFlags | PROC_MIGRATING) &
~PROC_MIGRATION_DONE;
/*
* Go through the buffer to deencapsulate the process module-by-module.
*/
bufPtr = inBufPtr->ptr;
for (i = 0; i < PROC_MIG_NUM_CALLBACKS; i++) {
infoPtr = (Proc_EncapInfo *) bufPtr;
if (infoPtr->token != encapCallbacks[i].token) {
/*
* This callback wasn't used.
*/
continue;
}
if (infoPtr->special) {
/*
* This is where we'd like to handle special cases like shared
* memory processes. For now, this should never happen.
*/
if (proc_MigDebugLevel > 0) {
panic("ProcMigReceiveProcess: special flag set?! (continuable -- but call Fred)");
}
procPtr->genFlags &= ~PROC_MIGRATING;
Proc_Unlock(procPtr);
return(PROC_MIGRATION_REFUSED);
}
if (proc_MigDebugLevel > 5) {
printf("Calling deencapFunc %d\n", i);
}
bufPtr += sizeof(Proc_EncapInfo);
status = (*encapCallbacks[i].deencapFunc)(procPtr, infoPtr, bufPtr);
#ifdef lint
status = DeencapProcState(procPtr, infoPtr, bufPtr);
status = ProcExecDeencapState(procPtr, infoPtr, bufPtr);
status = Vm_DeencapState(procPtr, infoPtr, bufPtr);
status = Fs_DeencapFileState(procPtr, infoPtr, bufPtr);
status = Mach_DeencapState(procPtr, infoPtr, bufPtr);
status = Prof_DeencapState(procPtr, infoPtr, bufPtr);
status = Sig_DeencapState(procPtr, infoPtr, bufPtr);
#endif /* lint */
if (status != SUCCESS) {
printf("Warning: ProcMigReceiveProcess: error returned by deencapsulation procedure %s:\n\t%s.\n",
#ifdef BREAKS_KDBX
callbackNames[i].deencapFunc,
#else /* BREAKS_KDBX */
"(can't get name)",
#endif /* BREAKS_KDBX */
Stat_GetMsg(status));
procPtr->genFlags &= ~PROC_MIGRATING;
Proc_Unlock(procPtr);
return(status);
}
bufPtr += infoPtr->size;
}
/*
* Update statistics.
*/
if (procPtr->genFlags & PROC_FOREIGN) {
PROC_MIG_INC_STAT(foreign);
#ifndef CLEAN
if (proc_MigDoStats) {
PROC_MIG_INC_STAT(imports);
}
#endif /* CLEAN */
} else {
#ifndef CLEAN
if (proc_MigDoStats) {
PROC_MIG_INC_STAT(returns);
PROC_MIG_DEC_STAT(remote);
}
#endif /* CLEAN */
}
procPtr->genFlags &= ~PROC_MIGRATING;
Proc_Unlock(procPtr);
return(status);
}
/*
* Define the process state that is sent during migration.
* See proc.h for explanations of these fields.
* This structure is followed by a variable-length string containing
* procPtr->argString, padded to an integer boundary.
*/
typedef struct {
int migFlags;
Proc_PID parentID;
int familyID;
int userID;
int effectiveUserID;
int genFlags;
int syncFlags;
int schedFlags;
int exitFlags;
int billingRate;
unsigned int recentUsage;
unsigned int weightedUsage;
unsigned int unweightedUsage;
Proc_Time kernelCpuUsage;
Proc_Time userCpuUsage;
Proc_Time childKernelCpuUsage;
Proc_Time childUserCpuUsage;
int numQuantumEnds;
int numWaitEvents;
unsigned int schedQuantumTicks;
Proc_TimerInterval timers[PROC_MAX_TIMER + 1];
int argStringLength;
int unixProgress;
} EncapState;
#define COPY_STATE(from, to, field) to->field = from->field
/*
* A process is allowed to update its userID, effectiveUserID,
* billingRate, or familyID. If any of these fields is modified, all
* of them are transferred to the remote host.
*/
typedef struct {
int familyID;
int userID;
int effectiveUserID;
int billingRate;
} UpdateEncapState;
/*
* Parameters for a remote Proc_Suspend or resume.
*/
typedef struct {
int termReason; /* Reason why process went to this state.*/
int termStatus; /* Termination status.*/
int termCode; /* Termination code. */
int flags; /* Exit flags. */
} SuspendInfo;
/*
* Extra info used for suspend callback.
*/
typedef struct {
Proc_PID processID; /* Process being suspended/resumed. */
SuspendInfo info; /* Info to pass to home machine. */
} SuspendCallbackInfo;
/*
*----------------------------------------------------------------------
*
* GetProcEncapSize --
*
* Determine the size of the encapsulated process state.
*
* Results:
* SUCCESS is returned directly; the size of the encapsulated state
* is returned in infoPtr->size.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
static ReturnStatus
GetProcEncapSize(procPtr, hostID, infoPtr)
Proc_ControlBlock *procPtr; /* process being migrated */
int hostID; /* host to which it migrates */
Proc_EncapInfo *infoPtr; /* area w/ information about
* encapsulated state */
{
infoPtr->size = sizeof(EncapState) +
Byte_AlignAddr(strlen(procPtr->argString) + 1);
/*
* The clientData part of the EncapInfo struct is used to indicate
* that the process is migrating home.
*/
if (procPtr->genFlags & PROC_FOREIGN) {
infoPtr->data = (ClientData) 1;
if (proc_MigDebugLevel > 4) {
printf("Encapsulating foreign process %x.\n", procPtr->processID);
}
} else {
infoPtr->data = (ClientData) 0;
if (proc_MigDebugLevel > 4) {
printf("Encapsulating local process %x.\n", procPtr->processID);
}
}
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* EncapProcState --
*
* Encapsulate the state of a process from the Proc_ControlBlock
* and return it in the buffer provided.
*
* Results:
* SUCCESS. The buffer is filled.
*
* Side effects:
* None.
*----------------------------------------------------------------------
*/
/* ARGSUSED */
static ReturnStatus
EncapProcState(procPtr, hostID, infoPtr, bufPtr)
register Proc_ControlBlock *procPtr; /* The process being migrated */
int hostID; /* host to which it migrates */
Proc_EncapInfo *infoPtr;
Address bufPtr;
{
int argStringLength;
EncapState *encapPtr = (EncapState *) bufPtr;
int i;
ReturnStatus status;
Proc_TimerInterval timer;
COPY_STATE(procPtr, encapPtr, migFlags);
COPY_STATE(procPtr, encapPtr, parentID);
COPY_STATE(procPtr, encapPtr, familyID);
COPY_STATE(procPtr, encapPtr, userID);
COPY_STATE(procPtr, encapPtr, effectiveUserID);
COPY_STATE(procPtr, encapPtr, genFlags);
COPY_STATE(procPtr, encapPtr, syncFlags);
COPY_STATE(procPtr, encapPtr, schedFlags);
COPY_STATE(procPtr, encapPtr, exitFlags);
COPY_STATE(procPtr, encapPtr, billingRate);
COPY_STATE(procPtr, encapPtr, recentUsage);
COPY_STATE(procPtr, encapPtr, weightedUsage);
COPY_STATE(procPtr, encapPtr, unweightedUsage);
COPY_STATE(procPtr, encapPtr, kernelCpuUsage);
COPY_STATE(procPtr, encapPtr, userCpuUsage);
COPY_STATE(procPtr, encapPtr, childKernelCpuUsage);
COPY_STATE(procPtr, encapPtr, childUserCpuUsage);
COPY_STATE(procPtr, encapPtr, numQuantumEnds);
COPY_STATE(procPtr, encapPtr, numWaitEvents);
COPY_STATE(procPtr, encapPtr, schedQuantumTicks);
COPY_STATE(procPtr, encapPtr, unixProgress);
/*
* Get the timer state in an easy-to-transfer form. Unlock
* the process first since ProcChangeTimer will lock it.
*/
timer.interval = time_ZeroSeconds;
timer.curValue = time_ZeroSeconds;
Proc_Unlock(procPtr);
for (i = 0; i <= PROC_MAX_TIMER; i++) {
status = ProcChangeTimer(i, &timer, &encapPtr->timers[i], FALSE);
#define DEBUG_TIMER
#ifdef DEBUG_TIMER
if ((encapPtr->timers[i].curValue.seconds < 0) ||
(encapPtr->timers[i].curValue.microseconds < 0) ||
(encapPtr->timers[i].curValue.microseconds > ONE_SECOND) ||
(encapPtr->timers[i].interval.seconds < 0) ||
(encapPtr->timers[i].interval.microseconds < 0) ||
(encapPtr->timers[i].interval.microseconds > ONE_SECOND)) {
panic("Migration error: timer value (<%d,%d>@<%d,%d>) is bad.\n",
encapPtr->timers[i].curValue.seconds,
encapPtr->timers[i].curValue.microseconds,
encapPtr->timers[i].interval.seconds,
encapPtr->timers[i].interval.microseconds);
Proc_Lock(procPtr);
return(FAILURE);
}
#endif /* DEBUG_TIMER */
if (status != SUCCESS) {
if (proc_MigDebugLevel > 0) {
printf("EncapProcState: error returned from ProcChangeTimer: %s.\n",
Stat_GetMsg(status));
}
Proc_Lock(procPtr);
return(status);
}
}
Proc_Lock(procPtr);
bufPtr += sizeof(EncapState);
argStringLength = Byte_AlignAddr(strlen(procPtr->argString) + 1);
encapPtr->argStringLength = argStringLength;
(void) strncpy(bufPtr, procPtr->argString, argStringLength);
/*
* If we're migrating away from home, subtract the process's current
* CPU usage so it can be added in again when the process returns
* here. Passing negative tick values seems like a relatively easy
* way to subtract time, though perhaps we should pass a separate parameter
* to ProcRecordUsage instead and call Timer_AddTicks or
* Timer_SubtractTicks depending on the parameter.
*/
#ifndef CLEAN
if (infoPtr->data == 0) {
Timer_Ticks ticks;
Timer_SubtractTicks(timer_TicksZeroSeconds,
procPtr->kernelCpuUsage.ticks,
&ticks);
Timer_SubtractTicks(ticks, procPtr->userCpuUsage.ticks,
&ticks);
ProcRecordUsage(ticks, PROC_MIG_USAGE_REMOTE_CPU);
}
#endif /* CLEAN */
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* DeencapProcState --
*
* Get information from a Proc_ControlBlock from another host.
* The information is contained in the parameter ``buffer''.
* The process control block should be locked on entry and exit.
*
* Results:
* Usually SUCCESS. Can propagate an error return from
* ProcChangeTimer. Returns PROC_MIGRATION_REFUSED if the
* process has been flagged as unmigratable and this is not its
* home node.
*
* Side effects:
* None.
*----------------------------------------------------------------------
*/
/* ARGSUSED */
static ReturnStatus
DeencapProcState(procPtr, infoPtr, bufPtr)
register Proc_ControlBlock *procPtr; /* The process being migrated */
Proc_EncapInfo *infoPtr; /* information about the buffer */
Address bufPtr; /* buffer containing data */
{
Boolean home;
EncapState *encapPtr = (EncapState *) bufPtr;
int i;
ReturnStatus status;
Timer_Ticks ticks;
if (infoPtr->data == 0) {
if (proc_MigDebugLevel > 4) {
printf("Deencapsulating foreign process %x.\n", procPtr->processID);
}
home = FALSE;
} else {
if (proc_MigDebugLevel > 4) {
printf("Deencapsulating local process %x.\n", procPtr->processID);
}
home = TRUE;
}
COPY_STATE(encapPtr, procPtr, migFlags);
COPY_STATE(encapPtr, procPtr, parentID);
COPY_STATE(encapPtr, procPtr, familyID);
COPY_STATE(encapPtr, procPtr, userID);
COPY_STATE(encapPtr, procPtr, effectiveUserID);
COPY_STATE(encapPtr, procPtr, genFlags);
COPY_STATE(encapPtr, procPtr, syncFlags);
COPY_STATE(encapPtr, procPtr, schedFlags);
COPY_STATE(encapPtr, procPtr, exitFlags);
COPY_STATE(encapPtr, procPtr, billingRate);
COPY_STATE(encapPtr, procPtr, recentUsage);
COPY_STATE(encapPtr, procPtr, weightedUsage);
COPY_STATE(encapPtr, procPtr, unweightedUsage);
COPY_STATE(encapPtr, procPtr, kernelCpuUsage);
COPY_STATE(encapPtr, procPtr, userCpuUsage);
COPY_STATE(encapPtr, procPtr, childKernelCpuUsage);
COPY_STATE(encapPtr, procPtr, childUserCpuUsage);
COPY_STATE(encapPtr, procPtr, numQuantumEnds);
COPY_STATE(encapPtr, procPtr, numWaitEvents);
COPY_STATE(encapPtr, procPtr, schedQuantumTicks);
COPY_STATE(encapPtr, procPtr, unixProgress);
/*
* The process should never be flagged as unmigratable, so
* complain if it is. Allow "unmigratable" processes to migrate
* back to the home node, but don't let them go anywhere else.
*/
if (procPtr->genFlags & PROC_DONT_MIGRATE) {
Sys_HostPrint(procPtr->peerHostID,
"wants to give us an unmigratable process.\n");
if (!home) {
return PROC_MIGRATION_REFUSED;
}
}
/*
* Set the effective process for this processor while doing the
* ProcChangeTimer since we're doing it on behalf of another
* process.
*/
Proc_SetEffectiveProc(procPtr);
Proc_Unlock(procPtr);
for (i = 0; i <= PROC_MAX_TIMER; i++) {
status = ProcChangeTimer(i, &encapPtr->timers[i],
(Proc_TimerInterval *) USER_NIL, FALSE);
if (status != SUCCESS) {
if (proc_MigDebugLevel > 0) {
printf("DeencapProcState: error returned from ProcChangeTimer: %s.\n",
Stat_GetMsg(status));
}
Proc_Lock(procPtr);
return(status);
}
}
Proc_Lock(procPtr);
Proc_SetEffectiveProc((Proc_ControlBlock *) NIL);
bufPtr += sizeof(*encapPtr);
if (procPtr->argString != (char *) NIL) {
free(procPtr->argString);
}
procPtr->argString = (char *) malloc(encapPtr->argStringLength);
bcopy(bufPtr, (Address) procPtr->argString, encapPtr->argStringLength);
procPtr->genFlags |= PROC_NO_VM;
if (home) {
procPtr->genFlags &= (~PROC_FOREIGN);
procPtr->kcallTable = mach_NormalHandlers;
} else {
procPtr->genFlags |= PROC_FOREIGN;
procPtr->kcallTable = mach_MigratedHandlers;
}
procPtr->genFlags &= ~PROC_MIG_PENDING;
procPtr->schedFlags &=
~(SCHED_STACK_IN_USE | SCHED_CONTEXT_SWITCH_PENDING);
/*
* Initialize some of the fields as if for a new process. If migrating
* home, these are already set up. Fix up dependencies.
*/
procPtr->state = PROC_NEW;
Vm_ProcInit(procPtr);
procPtr->event = NIL;
if (!home) {
/*
* Initialize our child list to remove any old links.
*/
List_Init((List_Links *) procPtr->childList);
} else {
/*
* Forget the dependency on the other host; we're running
* locally now.
*/
ProcMigRemoveDependency(procPtr->processID, TRUE);
/*
* Update remote CPU usage stats.
*/
#ifndef CLEAN
Timer_AddTicks(procPtr->kernelCpuUsage.ticks,
procPtr->userCpuUsage.ticks, &ticks);
ProcRecordUsage(ticks, PROC_MIG_USAGE_REMOTE_CPU);
#endif /* CLEAN */
if (procPtr->migFlags & PROC_EVICTING) {
procPtr->migFlags &= ~PROC_EVICTING;
#ifndef CLEAN
if (!(procPtr->migFlags & PROC_WAS_EVICTED)) {
procPtr->migFlags |= PROC_WAS_EVICTED;
procPtr->preEvictionUsage.ticks = ticks;
}
#endif /* CLEAN */
} else if (!home) {
procPtr->migFlags &= ~PROC_WAS_EVICTED;
}
}
if (proc_MigDebugLevel > 4) {
printf("Received process state for process %x.\n", procPtr->processID);
}
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* Proc_MigUpdateInfo --
*
* Send the updateable portions of the state of a process to the
* host on which it is currently executing. This requires
* packaging the relevant information from the Proc_ControlBlock
* and sending it via an RPC.
*
* The process is assumed to be locked.
*
* Results:
* A ReturnStatus is returned to indicate the status of the RPC.
*
* Side effects:
* A remote procedure call is performed and the process state
* is transferred.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Proc_MigUpdateInfo(procPtr)
Proc_ControlBlock *procPtr; /* The migrated process */
{
ReturnStatus status;
ProcMigCmd cmd;
UpdateEncapState state;
UpdateEncapState *statePtr = &state;
Proc_MigBuffer inBuf;
COPY_STATE(procPtr, statePtr, familyID);
COPY_STATE(procPtr, statePtr, userID);
COPY_STATE(procPtr, statePtr, effectiveUserID);
COPY_STATE(procPtr, statePtr, billingRate);
/*
* Set up for the RPC.
*/
cmd.command = PROC_MIGRATE_CMD_UPDATE;
cmd.remotePid = procPtr->peerProcessID;
inBuf.size = sizeof(UpdateEncapState);
inBuf.ptr = (Address) statePtr;
status = ProcMigCommand(procPtr->peerHostID, &cmd, &inBuf,
(Proc_MigBuffer *) NIL);
if (status != SUCCESS && proc_MigDebugLevel > 0) {
printf("Warning: Proc_MigUpdateInfo: error returned updating information on host %d:\n\t%s.\n",
procPtr->peerHostID,Stat_GetMsg(status));
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* ProcMigGetUpdate --
*
* Receive the updateable portions of the state of a process from its
* home node.
*
* Results:
* SUCCESS.
*
* Side effects:
* The process's control block is locked and then updated.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
ReturnStatus
ProcMigGetUpdate(cmdPtr, procPtr, inBufPtr, outBufPtr)
ProcMigCmd *cmdPtr;/* contains ID of process on this host */
Proc_ControlBlock *procPtr; /* ptr to process control block */
Proc_MigBuffer *inBufPtr; /* input buffer */
Proc_MigBuffer *outBufPtr; /* output buffer (stays NIL) */
{
UpdateEncapState *statePtr = (UpdateEncapState *) inBufPtr->ptr;
Proc_Lock(procPtr);
COPY_STATE(statePtr, procPtr, familyID);
COPY_STATE(statePtr, procPtr, userID);
COPY_STATE(statePtr, procPtr, effectiveUserID);
COPY_STATE(statePtr, procPtr, billingRate);
Proc_Unlock(procPtr);
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* ProcRemoteSuspend --
*
* Tell the home node of a process that it has been suspended or resumed.
* This routine is called from within the signal handling routines.
*
* Results:
* None.
*
* Side effects:
* Sets up a background process to make an RPC.
*
*----------------------------------------------------------------------
*/
void
ProcRemoteSuspend(procPtr, exitFlags)
register Proc_ControlBlock *procPtr; /* Process whose state is changing. */
int exitFlags; /* Flags to set for child. */
{
ReturnStatus status;
SuspendCallbackInfo *callPtr; /* Information for the callback. */
SuspendInfo *infoPtr; /* Info to pass back. */
if (proc_MigDebugLevel > 4) {
printf("ProcRemoteSuspend(%x) called.\n", procPtr->processID);
}
status = Recov_IsHostDown(procPtr->peerHostID);
if (status != SUCCESS) {
if (proc_MigDebugLevel > 0) {
printf("ProcRemoteSuspend: host %d is down; killing process %x.\n",
procPtr->peerHostID, procPtr->processID);
}
/*
* Perform an exit on behalf of the process -- it's not
* in a state where we can signal it. The process is
* unlocked as a side effect.
*/
ProcExitProcess(procPtr, PROC_TERM_DESTROYED, (int) PROC_NO_PEER, 0,
FALSE);
/*
* This point should not be reached, but the N-O-T-R-E-A-C-H-E-D
* directive causes a complaint when there's code after it.
*/
panic("ProcRemoteSuspend: Proc_ExitInt returned.\n");
return;
}
callPtr = (SuspendCallbackInfo *) malloc(sizeof(SuspendCallbackInfo));
infoPtr = &callPtr->info;
callPtr->processID = procPtr->processID;
COPY_STATE(procPtr, infoPtr, termReason);
COPY_STATE(procPtr, infoPtr, termStatus);
COPY_STATE(procPtr, infoPtr, termCode);
infoPtr->flags = exitFlags;
Proc_CallFunc(SuspendCallback, (ClientData) callPtr, 0);
}
/*
*----------------------------------------------------------------------
*
* SuspendCallback --
*
* Tell the home node of a process that it has been suspended or resumed.
* This is called via a Proc_CallFunc so the signal monitor lock
* is not held.
*
* Results:
* None.
*
* Side effects:
* A remote procedure call is performed.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
static void
SuspendCallback(data, callInfoPtr)
ClientData data;
Proc_CallInfo *callInfoPtr; /* not used */
{
SuspendCallbackInfo *callPtr; /* Pointer to callback info. */
register Proc_ControlBlock *procPtr; /* Process whose state is changing. */
ReturnStatus status;
int numTries; /* number of times trying RPC */
ProcMigCmd cmd;
Proc_MigBuffer inBuf;
int host = 0;
callPtr = (SuspendCallbackInfo *) data;
if (proc_MigDebugLevel > 4) {
printf("SuspendCallback(%x) called.\n", callPtr->processID);
}
procPtr = Proc_LockPID(callPtr->processID);
if (procPtr == (Proc_ControlBlock *) NIL) {
if (proc_MigDebugLevel > 1) {
printf("SuspendCallback: no such process (%x).\n",
callPtr->processID);
}
status = PROC_NO_PEER;
goto done;
}
host = procPtr->peerHostID;
cmd.remotePid = procPtr->peerProcessID;
/*
* Now that we have the relevant info, unlock the process while we're
* doing the RPC. We don't need it anymore anyway.
*/
Proc_Unlock(procPtr);
/*
* Set up for the RPC.
*/
cmd.command = PROC_MIGRATE_CMD_SUSPEND;
inBuf.size = sizeof(SuspendInfo);
inBuf.ptr = (Address) &callPtr->info;
for (numTries = 0; numTries < PROC_MAX_RPC_RETRIES; numTries++) {
status = ProcMigCommand(host, &cmd, &inBuf,
(Proc_MigBuffer *) NIL);
if (status != RPC_TIMEOUT) {
break;
}
status = Proc_WaitForHost(host);
if (status != SUCCESS) {
break;
}
}
done:
if (status != SUCCESS && proc_MigDebugLevel > 2) {
printf("Warning: SuspendCallback: error returned passing suspend to host %d:\n\t%s.\n",
host,Stat_GetMsg(status));
} else if (proc_MigDebugLevel > 4) {
printf("SuspendCallback(%x) completed successfully.\n",
callPtr->processID);
}
free ((Address) callPtr);
}
/*
*----------------------------------------------------------------------
*
* ProcMigGetSuspend --
*
* Receive the new exit status of a process from its remote node.
*
* Results:
* SUCCESS.
*
* Side effects:
* The process's control block is locked and then updated.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
ReturnStatus
ProcMigGetSuspend(cmdPtr, procPtr, inBufPtr, outBufPtr)
ProcMigCmd *cmdPtr;/* contains ID of process on this host */
Proc_ControlBlock *procPtr; /* ptr to process control block */
Proc_MigBuffer *inBufPtr; /* input buffer */
Proc_MigBuffer *outBufPtr; /* output buffer (stays NIL) */
{
register SuspendInfo *infoPtr = (SuspendInfo *) inBufPtr->ptr;
Proc_Lock(procPtr);
COPY_STATE(infoPtr, procPtr, termReason);
COPY_STATE(infoPtr, procPtr, termStatus);
COPY_STATE(infoPtr, procPtr, termCode);
Proc_InformParent(procPtr, infoPtr->flags);
Proc_Unlock(procPtr);
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* ProcMigEncapCallback --
*
* Handle a callback on behalf of a module requesting more data.
* Not yet implemented.
*
* Results:
* A ReturnStatus, dependent on the module doing the callback.
*
* Side effects:
* Variable.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
ReturnStatus
ProcMigEncapCallback(cmdPtr, procPtr, inBufPtr, outBufPtr)
ProcMigCmd *cmdPtr;/* contains ID of process on this host */
Proc_ControlBlock *procPtr; /* ptr to process control block */
Proc_MigBuffer *inBufPtr; /* input buffer */
Proc_MigBuffer *outBufPtr; /* output buffer (stays NIL) */
{
return(FAILURE);
}
/*
*----------------------------------------------------------------------
*
* ProcMigKillRemoteCopy --
*
* Inform the remote host that a failure occurred during migration,
* so the incomplete process on the remote host will kill the process.
* This just sets up and issues a MigCommand.
*
* Results:
* None. The caller doesn't normally care about the status of the
* RPC.
*
* Side effects:
* A remote procedure call is performed and the migrated process
* is killed.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
void
ProcMigKillRemoteCopy(data, infoPtr)
ClientData data; /* The ID of the remote process */
Proc_CallInfo *infoPtr; /* unused. */
{
Proc_PID processID = (Proc_PID) data;
ReturnStatus status;
ProcMigCmd cmd;
int hostID; /* Host to notify. */
/*
* Set up for the RPC.
*/
cmd.command = PROC_MIGRATE_CMD_DESTROY;
cmd.remotePid = processID;
hostID = Proc_GetHostID(processID);
status = ProcMigCommand(hostID, &cmd, (Proc_MigBuffer *) NIL,
(Proc_MigBuffer *) NIL);
if (status != SUCCESS && proc_MigDebugLevel > 2) {
printf("Warning: KillRemoteCopy: error returned by Rpc_Call: %s.\n",
Stat_GetMsg(status));
}
}
/*
*----------------------------------------------------------------------
*
* Proc_FlagMigration --
*
* Mark a process as waiting to migrate. This will cause the process
* to trap to the Proc_MigrateTrap routine after the next time it
* traps into the kernel.
*
* The calling routine is assumed to hold the lock for the process.
*
* Results:
* None.
*
* Side effects:
* The process is flagged for migration. If the process is suspended,
* it is resumed.
*
*----------------------------------------------------------------------
*/
void
Proc_FlagMigration(procPtr, hostID, exec)
Proc_ControlBlock *procPtr; /* The process being migrated */
int hostID; /* Host to which it migrates */
Boolean exec; /* Whether it's doing a remote exec */
{
procPtr->genFlags |= PROC_MIG_PENDING;
procPtr->genFlags &= ~PROC_MIGRATION_DONE;
if (exec) {
/*
* We flag the process specially so we know to copy over exec
* arguments.
*/
procPtr->genFlags |= PROC_REMOTE_EXEC_PENDING;
}
procPtr->peerHostID = hostID;
if (procPtr->state == PROC_SUSPENDED) {
Sig_SendProc(procPtr, SIG_RESUME, 0, (Address)0);
}
Sig_SendProc(procPtr, SIG_MIGRATE_TRAP, 0, (Address)0);
Sig_AllowMigration(procPtr);
}
/*
*----------------------------------------------------------------------
*
* ProcInitiateMigration --
*
* Send a message to a specific workstation requesting permission to
* migrate a process.
*
* Results:
* SUCCESS is returned if permission is granted.
* PROC_MIGRATION_REFUSED is returned if the host is not accepting
* migrated processes or it is not at the right migration
* level.
* GEN_INVALID_ID if the user doesn't have permission to migrate
* from this host or to the other host.
* Other errors may be returned by the rpc module.
*
* Side effects:
* A message is sent to the remote workstation.
*
*----------------------------------------------------------------------
*/
ReturnStatus
ProcInitiateMigration(procPtr, hostID)
register Proc_ControlBlock *procPtr; /* process to migrate */
int hostID; /* host to which to migrate */
{
ReturnStatus status;
ProcMigInitiateCmd init;
ProcMigCmd cmd;
Proc_MigBuffer inBuf;
Proc_MigBuffer outBuf;
Proc_PID pid;
int foreign;
init.processID = procPtr->processID;
init.version = proc_MigrationVersion;
init.userID = procPtr->userID;
init.clientID = rpc_SpriteID;
if (procPtr->genFlags & PROC_FOREIGN) {
foreign = 1;
cmd.remotePid = procPtr->peerProcessID;
} else {
foreign = 0;
cmd.remotePid = (Proc_PID) NIL;
}
cmd.command = PROC_MIGRATE_CMD_INIT;
inBuf.ptr = (Address) &init;
inBuf.size = sizeof(ProcMigInitiateCmd);
outBuf.ptr = (Address) &pid;
outBuf.size = sizeof(Proc_PID);
status = ProcMigCommand(hostID, &cmd, &inBuf, &outBuf);
if (status != SUCCESS) {
if (proc_MigDebugLevel > 2) {
printf(
"%s ProcInitiateMigration: Error returned by host %d:\n\t%s\n",
"Warning:", hostID, Stat_GetMsg(status));
}
} else if (!foreign) {
procPtr->peerProcessID = pid;
}
return(status);
}
/*
*----------------------------------------------------------------------
*
* ProcMigCommand --
*
* Send a process migration-related command to another host.
* This sets up and performs the RPC itself.
*
* RPC: Input parameters:
* process ID
* command to perform
* data buffer
* Return parameters:
* ReturnStatus
* data buffer
*
* Results:
* A ReturnStatus is returned to indicate the status of the RPC.
* The data buffer is filled by the RPC if a result is returned by
* the other host.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ReturnStatus
ProcMigCommand(host, cmdPtr, inPtr, outPtr)
int host; /* host to send command to */
ProcMigCmd *cmdPtr; /* command to send */
Proc_MigBuffer *inPtr; /* pair of <size, ptr> for input */
Proc_MigBuffer *outPtr; /* pair of <size, ptr> for output */
{
ReturnStatus status;
Rpc_Storage storage;
Proc_TraceRecord record;
int maxSize;
int toSend;
#ifndef CLEAN
if (proc_DoTrace && proc_MigDebugLevel > 2) {
record.processID = cmdPtr->remotePid;
record.flags = PROC_MIGTRACE_START;
record.info.command.type = cmdPtr->command;
record.info.command.data = (ClientData) NIL;
Trace_Insert(proc_TraceHdrPtr, PROC_MIGTRACE_COMMAND,
(ClientData) &record);
}
#endif /* CLEAN */
Rpc_MaxSizes(&maxSize, (int *) NIL);
/*
* Set up for the RPC.
*/
storage.requestParamPtr = (Address) cmdPtr;
storage.requestParamSize = sizeof(ProcMigCmd);
if (inPtr == (Proc_MigBuffer *) NIL) {
storage.requestDataPtr = (Address) NIL;
storage.requestDataSize = 0;
cmdPtr->totalSize = 0;
toSend = 0;
} else {
toSend = inPtr->size;
cmdPtr->totalSize = toSend;
}
cmdPtr->offset = 0;
storage.replyParamPtr = (Address) NIL;
storage.replyParamSize = 0;
/*
* Send the command, breaking it into sizes of at most size maxSize.
* Only the last "fragment" can actually return any data.
*/
do {
if ((toSend > maxSize) || (outPtr == (Proc_MigBuffer *) NIL)) {
storage.replyDataPtr = (Address) NIL;
storage.replyDataSize = 0;
} else {
storage.replyDataPtr = outPtr->ptr;
storage.replyDataSize = outPtr->size;
}
if (inPtr != (Proc_MigBuffer *) NIL) {
storage.requestDataPtr = inPtr->ptr + cmdPtr->offset;
storage.requestDataSize = (toSend > maxSize) ? maxSize : toSend;
}
if (proc_MigDebugLevel > 2) {
printf("cmd %d totalSize %d offset %d thisDataSize %d\n",
cmdPtr->command, cmdPtr->totalSize, cmdPtr->offset,
storage.requestDataSize);
}
status = Rpc_Call(host, RPC_PROC_MIG_COMMAND, &storage);
#ifndef CLEAN
if (proc_DoTrace && proc_MigDebugLevel > 2) {
record.flags = 0;
Trace_Insert(proc_TraceHdrPtr, PROC_MIGTRACE_COMMAND,
(ClientData) &record);
}
#endif /* CLEAN */
if (status != SUCCESS) {
if (proc_MigDebugLevel > 6) {
printf("%s ProcMigCommand: error %x returned by Rpc_Call.\n",
"Warning:", status);
}
return(status);
}
toSend -= maxSize;
cmdPtr->offset += maxSize;
} while (toSend > 0);
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* Proc_WaitForMigration --
*
* Wait for a process to migrate. Locks the process and
* then calls a monitored procedure.
* Note: this routine should not be used to verify that an arbitrary
* process is migrated (RpcProcFork doesn't set PROC_MIGRATION_DONE).
*
* Results:
* Returns a Sprite status code. SUCCESS means that the process has
* context switched to PROC_MIGRATED.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Proc_WaitForMigration(processID)
Proc_PID processID;
{
Proc_ControlBlock *procPtr;
ReturnStatus status;
procPtr = Proc_LockPID(processID);
if (procPtr == (Proc_ControlBlock *) NIL) {
return(PROC_INVALID_PID);
}
/*
* While in the middle of migration, wait on the condition
* and then recheck the flags and the processID.
* This avoids the possibility of the procPtr getting recycled while
* we're waiting.
*/
while (procPtr->genFlags & (PROC_MIG_PENDING | PROC_MIGRATING)) {
Proc_Unlock(procPtr);
status = WaitForMigration();
if (status != SUCCESS) {
return(status);
}
Proc_Lock(procPtr);
if (procPtr->processID != processID) {
Proc_Unlock(procPtr);
return(PROC_INVALID_PID);
}
}
if ((procPtr->genFlags & PROC_MIGRATION_DONE) &&
(procPtr->state == PROC_MIGRATED)) {
status = SUCCESS;
} else {
status = FAILURE;
}
Proc_Unlock(procPtr);
return(status);
}
/*
*----------------------------------------------------------------------
*
* WaitForMigration --
*
* Monitored procedure to wait for a migration condition to be
* signalled. Higher-level locking actually guarantees that
* a process has actually migrated.
*
* Results:
* SUCCESS, or GEN_ABORTED_BY_SIGNAL.
*
* Side effects:
* Puts current process to sleep.
*
*----------------------------------------------------------------------
*/
static ENTRY ReturnStatus
WaitForMigration()
{
ReturnStatus status;
LOCK_MONITOR;
if (Sync_Wait(&migrateCondition, TRUE)) {
status = GEN_ABORTED_BY_SIGNAL;
} else {
status = SUCCESS;
}
UNLOCK_MONITOR;
return(status);
}
/*
*----------------------------------------------------------------------
*
* AddMigrateTime --
*
* Monitored procedure to add a time to the statistics structure
* atomically.
*
* Results:
* None.
*
* Side effects:
* Adds time.
*
*----------------------------------------------------------------------
*/
static ENTRY void
AddMigrateTime(time, totalPtr, squaredTotalPtr)
Time time;
unsigned int *totalPtr;
unsigned int *squaredTotalPtr;
{
unsigned int intTime;
unsigned int squaredTime;
#ifndef CLEAN
LOCK_MONITOR;
/*
* Round times to hundreds of milliseconds, to keep things
* on a low enough scale to keep from overflowing too easily.
*/
intTime = PROC_MIG_TIME_FOR_STATS(time);
*totalPtr += intTime;
squaredTime = intTime * intTime;
*squaredTotalPtr += squaredTime;
UNLOCK_MONITOR;
#endif /* CLEAN */
}
/*
*----------------------------------------------------------------------
*
* Proc_MigAddToCounter --
*
* Monitored procedure to add a value to a global variable.
* This keeps statistics from being trashed if this were
* executed on a multiprocessor, since incrementing a counter
* isn't necessarily atomic. If squaredPtr is non-NIL, it
* adds the square of the value to that variable.
*
* Results:
* None.
*
* Side effects:
* Updates variable pointed to by intPtr & squaredPtr.
*
*----------------------------------------------------------------------
*/
ENTRY void
Proc_MigAddToCounter(value, intPtr, squaredPtr)
int value;
unsigned int *intPtr;
unsigned int *squaredPtr;
{
LOCK_MONITOR;
*intPtr += value;
if (squaredPtr != (unsigned int *) NIL) {
*squaredPtr += value * value;
}
UNLOCK_MONITOR;
}
/*
*----------------------------------------------------------------------
*
* ProcRecordUsage --
*
* Specialized procedure to update global CPU usages
* atomically. Because we do some funny arithmetic to store
* the square of a time, we convert timer ticks into times and
* use the function defined above.
*
* Results:
* None.
*
* Side effects:
* Adds values.
*
*----------------------------------------------------------------------
*/
void
ProcRecordUsage(ticks, type)
Timer_Ticks ticks;
ProcRecordUsageType type;
{
unsigned int *timePtr = (unsigned int *) NIL;
unsigned int *squaredTimePtr = (unsigned int *) NIL;
Time time;
#ifndef CLEAN
if (type == PROC_MIG_USAGE_REMOTE_CPU) {
timePtr = &proc_MigStats.varStats.remoteCPUTime;
squaredTimePtr = &proc_MigStats.squared.remoteCPUTime;
} else if (type == PROC_MIG_USAGE_TOTAL_CPU) {
timePtr = &proc_MigStats.varStats.totalCPUTime;
squaredTimePtr = &proc_MigStats.squared.totalCPUTime;
proc_MigStats.processes++;
} else if (type == PROC_MIG_USAGE_POST_EVICTION) {
timePtr = &proc_MigStats.varStats.evictionCPUTime;
squaredTimePtr = &proc_MigStats.squared.evictionCPUTime;
proc_MigStats.evictionsToUs++;
}
Timer_TicksToTime(ticks, &time);
AddMigrateTime(time, timePtr, squaredTimePtr);
#endif /* CLEAN */
}
/*
*----------------------------------------------------------------------
*
* AccessStats --
*
* Access the migration statistics structure atomically. Individual
* fields may be incremented or decremented outside the lock, but
* looking at the whole structure synchronizes with actions that
* operate on double words, as does resetting it to 0.
*
* If copyPtr is NIL, then reset the stats, else copy them.
*
* Results:
* If requested, a copy of the statistics structure is returned to
* the caller.
*
* Side effects:
* If requested, the statistics structure is zeroed.
*
*----------------------------------------------------------------------
*/
static ENTRY void
AccessStats(copyPtr)
Proc_MigStats *copyPtr; /* pointer to area to copy stats into, or NIL */
{
LOCK_MONITOR;
if (copyPtr != (Proc_MigStats *) NIL) {
bcopy((Address) &proc_MigStats, (Address) copyPtr,
sizeof(Proc_MigStats));
} else {
bzero((Address) &proc_MigStats, sizeof(Proc_MigStats));
proc_MigStats.statsVersion = statsVersion;
}
UNLOCK_MONITOR;
}
/*
*----------------------------------------------------------------------
*
* Proc_MigGetStats --
*
* Return migration statistics to the user.
*
* Results:
* SUCCESS, unless there's a problem copying to user space.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Proc_MigGetStats(addr)
Address addr;
{
Proc_MigStats copy;
ReturnStatus status;
AccessStats(©);
status = Vm_CopyOut(sizeof(Proc_MigStats),
(Address)©,
addr);
return(status);
}
/*
*----------------------------------------------------------------------
*
* Proc_MigResetStats --
*
* Zero the migration statistics structure.
*
* Results:
* SUCCESS.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Proc_MigResetStats()
{
AccessStats((Proc_MigStats *) NIL);
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* ProcMigWakeupWaiters --
*
* Monitored procedure to signal any processes that may have waited for
* a process to migrate.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ENTRY void
ProcMigWakeupWaiters()
{
LOCK_MONITOR;
Sync_Broadcast(&migrateCondition);
UNLOCK_MONITOR;
}
/*
*----------------------------------------------------------------------
*
* Proc_MigrateStartTracing --
*
* Initialize the tracing variables for process migration.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
void
Proc_MigrateStartTracing()
{
static Boolean init = FALSE;
if (!init) {
init = TRUE;
proc_TraceHdrPtr = &proc_TraceHeader;
Trace_Init(proc_TraceHdrPtr, PROC_NUM_TRACE_RECS,
sizeof(Proc_TraceRecord), 0);
}
proc_DoTrace = TRUE;
}
/*
*----------------------------------------------------------------------
*
* Proc_DestroyMigratedProc --
*
* Kill a process, presumably when its peer host (the home host
* of a foreign process, or the remote host of a migrated process)
* is down. It may also be done if the process is
* unsuccessfully killed with a signal, even if the remote host
* hasn't been down long enough to be sure it has crashed.
*
* This procedure is distinct from Proc_KillRemoteCopy, which issues
* a command to do a similar thing on the host to which the process
* is migrating. In this case, we're killing our own copy of it.
*
* Results:
* None.
*
* Side effects:
* The process is killed.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
void
Proc_DestroyMigratedProc(pidData, callInfoPtr)
ClientData pidData; /* the process ID, as a ClientData */
Proc_CallInfo *callInfoPtr; /* Not used. */
{
Proc_ControlBlock *procPtr; /* Process to kill. */
Proc_PID pid = (Proc_PID) pidData;
procPtr = Proc_LockPID(pid);
if (procPtr == (Proc_ControlBlock *) NIL) {
if (proc_MigDebugLevel > 0) {
printf("Warning: Proc_DestroyMigratedProc: process %x not found.\n",
(int) pid);
}
/*
* Make sure the dependency on this process goes away.
*/
ProcMigRemoveDependency(pid, TRUE);
return;
}
if ((procPtr->state != PROC_MIGRATED) &&
!(procPtr->genFlags & PROC_FOREIGN)) {
if (procPtr->genFlags & PROC_MIGRATION_DONE) {
/*
* Just about to complete the migration.
*/
procPtr->genFlags |= PROC_MIG_ERROR;
if (proc_MigDebugLevel > 1) {
printf("%s Proc_DestroyMigratedProc: process %x not done migrating.\n",
"Warning:", (int) pid);
}
} else {
if (proc_MigDebugLevel > 0) {
printf("%s Proc_DestroyMigratedProc: process %x not migrated.\n",
"Warning:", (int) pid);
}
}
Proc_Unlock(procPtr);
/*
* Make sure the dependency on this process goes away.
*/
ProcMigRemoveDependency(pid, TRUE);
return;
}
if (procPtr->state == PROC_NEW && (procPtr->genFlags & PROC_FOREIGN)) {
/*
* The process was only partially migrated.
*/
if (procPtr->remoteExecBuffer != (Address) NIL) {
free(procPtr->remoteExecBuffer);
procPtr->remoteExecBuffer = (Address) NIL;
}
procPtr->state = PROC_DEAD;
Proc_CallFunc(Proc_Reaper, (ClientData) procPtr, 0);
Proc_Unlock(procPtr);
/*
* Make sure the dependency on this process goes away.
*/
ProcMigRemoveDependency(pid, TRUE);
return;
}
if (procPtr->state == PROC_MIGRATED) {
/*
* Perform an exit on behalf of the process -- it's not
* in a state where we can signal it. The process is
* unlocked as a side effect. We tell
* the recovery system that it should try later on to
* notify the other host since we aren't able to right now.
*/
ProcExitProcess(procPtr, PROC_TERM_DESTROYED, (int) PROC_NO_PEER, 0,
FALSE);
ProcMigRemoveDependency(pid, FALSE);
/*
* Update statistics.
*/
#ifndef CLEAN
if (proc_MigDoStats) {
PROC_MIG_DEC_STAT(remote);
}
#endif /* CLEAN */
} else {
/*
* Let it get killed the normal way, and let the exit routines
* handle cleaning up dependencies.
*/
Sig_SendProc(procPtr, SIG_KILL, (int) PROC_NO_PEER, (Address)0);
Proc_Unlock(procPtr);
}
}
/*
*----------------------------------------------------------------------
*
* Proc_EvictForeignProcs --
*
* Evict all foreign processes from this machine. To do this, send
* each foreign process the SIG_MIGRATE_HOME signal.
*
* Results:
* If sending any signals returns a non-SUCCESS status, that status
* is returned. Otherwise, SUCCESS is returned.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Proc_EvictForeignProcs()
{
ReturnStatus status;
int numEvicted; /* Not used */
#ifndef CLEAN
if (proc_MigDoStats) {
PROC_MIG_INC_STAT(evictCalls);
}
#endif /* CLEAN */
if (proc_MigStats.foreign == 0) {
if (proc_MigDebugLevel > 2) {
printf("Proc_EvictForeignProcs: no foreign processes.\n");
}
return(SUCCESS);
}
if (EvictionStarted()) {
if (proc_MigDebugLevel > 0) {
printf("Warning: eviction already in progress.\n");
}
/*
* We really should wait for the previous one to complete and then
* start over. For now, just tell the user we couldn't do it.
*/
return(FAILURE);
}
status = Proc_DoForEveryProc(Proc_IsEvictable, Proc_EvictProc, TRUE,
&numEvicted);
Proc_MigAddToCounter(numEvicted, &proc_MigStats.varStats.evictions,
&proc_MigStats.squared.evictions);
WaitForEviction();
return(status);
}
/*
*----------------------------------------------------------------------
*
* Proc_IsEvictable --
*
* Return whether the specified process is foreign and is okay
* to migrate. (This routine is a callback procedure that may be
* passed as a parameter to routines requiring an arbitrary
* Boolean procedure operating on a PCB.)
*
* Results:
* Boolean result: TRUE if foreign, FALSE if not.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
Boolean
Proc_IsEvictable(procPtr)
Proc_ControlBlock *procPtr;
{
if ((procPtr->genFlags & PROC_FOREIGN) &&
!(procPtr->genFlags & PROC_DONT_MIGRATE)) {
return(TRUE);
} else {
return(FALSE);
}
}
/*
*----------------------------------------------------------------------
*
* Proc_EvictProc --
*
* Send a process the SIG_MIGRATE_HOME signal. Note that if
* the process is not foreign, then the signal will be ignored.
* (This routine is a callback procedure that may be passed as a
* parameter to routines requiring an arbitrary procedure
* operating on a Proc_PID and returning a ReturnStatus.)
*
* Results:
* The value from Sig_Send is returned.
*
* Side effects:
* The specified process is signalled.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Proc_EvictProc(pid)
Proc_PID pid;
{
ReturnStatus status = SUCCESS;
Proc_ControlBlock *procPtr;
procPtr = Proc_LockPID(pid);
if (procPtr == (Proc_ControlBlock *) NIL) {
if (proc_MigDebugLevel > 2) {
printf("Proc_EvictProc: process %x no longer exists.\n", pid);
}
return (PROC_INVALID_PID);
}
if (proc_MigDebugLevel > 2) {
printf("Proc_EvictProc: evicting process %x.\n", pid);
}
if ((procPtr->genFlags & PROC_FOREIGN) &&
!(procPtr->genFlags & (PROC_DONT_MIGRATE | PROC_DYING)) &&
!(procPtr->migFlags & PROC_EVICTING)) {
procPtr->migFlags |= PROC_EVICTING;
PROC_MIG_INC_STAT(evictionsInProgress);
status = Sig_SendProc(procPtr, SIG_MIGRATE_HOME, 0, (Address)0);
}
Proc_Unlock(procPtr);
return(status);
}
/*
*----------------------------------------------------------------------
*
* EvictionStarted --
*
* Monitored procedure to initialize variables for recording
* eviction times.
*
* Results:
* TRUE if an eviction was already in progress, else FALSE.
*
* Side effects:
* The file-global evictionStarted time variable is initialized.
*
*----------------------------------------------------------------------
*/
static ENTRY Boolean
EvictionStarted()
{
LOCK_MONITOR;
if (proc_MigStats.evictionsInProgress != 0) {
UNLOCK_MONITOR;
return(TRUE);
}
#ifndef CLEAN
if (proc_MigDoStats) {
Timer_GetTimeOfDay(&timeEvictionStarted, (int *) NIL, (Boolean *) NIL);
}
#endif /* CLEAN */
UNLOCK_MONITOR;
return(FALSE);
}
/*
*----------------------------------------------------------------------
*
* WaitForEviction --
*
* Monitored procedure to record eviction times after eviction has
* completed.
*
* Results:
* None.
*
* Side effects:
* The time taken for eviction is added to the statistics structure.
*
*----------------------------------------------------------------------
*/
static ENTRY void
WaitForEviction()
{
Time time;
LOCK_MONITOR;
if (proc_MigStats.evictionsInProgress == 0) {
UNLOCK_MONITOR;
return;
}
while (proc_MigStats.evictionsInProgress != 0) {
if (Sync_Wait(&evictCondition, TRUE)) {
/*
* Interrupted. Just give up.
*/
proc_MigStats.evictionsInProgress = 0;
UNLOCK_MONITOR;
return;
}
}
#ifndef CLEAN
if (proc_MigDoStats) {
int intTime;
int squaredTime;
Timer_GetTimeOfDay(&time, (int *) NIL, (Boolean *) NIL);
Time_Subtract(time, timeEvictionStarted, &time);
intTime = PROC_MIG_TIME_FOR_STATS(time);
squaredTime = intTime * intTime;
proc_MigStats.varStats.totalEvictTime += intTime;
proc_MigStats.squared.totalEvictTime += squaredTime;
proc_MigStats.evictsNeeded++;
}
#endif /* CLEAN */
UNLOCK_MONITOR;
}
/*
*----------------------------------------------------------------------
*
* ProcMigEvictionComplete --
*
* Monitored procedure to signal the process that is recording eviction
* statistics. This is done any time an eviction completes. When
* the count of evictions hits zero, we wake up the process waiting for
* eviction. If the count of foreign processes ever hits 0 we also
* know all evictions are complete -- this is a double-check against
* losing track of a process during eviction if something unexpected
* happens (such as if it gets "destroyed").
*
* Results:
* None.
*
* Side effects:
* Notifies waiting process.
*
*----------------------------------------------------------------------
*/
ENTRY void
ProcMigEvictionComplete()
{
LOCK_MONITOR;
if (proc_MigStats.foreign == 0) {
proc_MigStats.evictionsInProgress = 0;
} else if (proc_MigStats.evictionsInProgress != 0) {
proc_MigStats.evictionsInProgress--;
}
if (proc_MigStats.evictionsInProgress == 0) {
Sync_Broadcast(&evictCondition);
}
UNLOCK_MONITOR;
}
/*
*----------------------------------------------------------------------
*
* Proc_NeverMigrate --
*
* Flag a process so it will never be migrated. This may be
* used to keep the master of a pseudo-device from migrating, or
* a process with kernel addresses mapped into user space from
* migrating. The process is flagged as unmigrateable for the rest of
* the lifetime of the process.
*
* Results:
* None.
*
* Side effects:
* The process's genFlags field is modified.
*
*----------------------------------------------------------------------
*/
void
Proc_NeverMigrate(procPtr)
Proc_ControlBlock *procPtr;
{
Proc_Lock(procPtr);
if (proc_MigDebugLevel > 4) {
printf("Proc_NeverMigrate: don't migrate process %x.\n",
procPtr->processID);
}
if (!(procPtr->genFlags & PROC_DONT_MIGRATE)) {
procPtr->genFlags |= PROC_DONT_MIGRATE;
if (procPtr->genFlags & PROC_FOREIGN) {
if (proc_MigDebugLevel > 3) {
printf("Proc_NeverMigrate: process %x is foreign.\n",
procPtr->processID);
}
PROC_MIG_DEC_STAT(foreign);
}
}
Proc_Unlock(procPtr);
}
/*
*----------------------------------------------------------------------
*
* Proc_GetEffectiveProc --
*
* Get a pointer to the Proc_ControlBlock for the process that is
* *effectively* running on the current processor. Thus, for an
* RPC server performing a system call on behalf of a migrated process,
* the "effective" process will be the process that invoked the system
* call. In all other cases, the "effective" process will be the
* same as the "actual" process.
*
* Results:
* A pointer to the process is returned. If no process is active,
* NIL is returned.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
Proc_ControlBlock *
Proc_GetEffectiveProc()
{
Proc_ControlBlock *procPtr;
procPtr = proc_RunningProcesses[Mach_GetProcessorNumber()];
if (procPtr == (Proc_ControlBlock *) NIL ||
procPtr->rpcClientProcess == (Proc_ControlBlock *) NIL) {
return(procPtr);
}
return(procPtr->rpcClientProcess);
}
/*
*----------------------------------------------------------------------
*
* Proc_SetEffectiveProc --
*
* Set the "effective" process on the current processor. If the
* process is (Proc_ControlBlock) NIL, the effective process is
* the same as the real process.
*
* Results:
* None.
*
* Side effects:
* The "rpcClientProcess" field of the current process's
* Proc_ControlBlock is set to hold the effective process.
*
*----------------------------------------------------------------------
*/
void
Proc_SetEffectiveProc(procPtr)
Proc_ControlBlock *procPtr;
{
Proc_ControlBlock *actualProcPtr;
actualProcPtr = Proc_GetActualProc();
if (actualProcPtr == (Proc_ControlBlock *) NIL) {
panic("Proc_SetEffectiveProcess: current process is NIL.\n");
} else {
actualProcPtr->rpcClientProcess = procPtr;
}
}